package debtbundler.presentation;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

import debtbundler.domain.bll.Utils;
import debtbundler.domain.bll.solver.HybridSolver;
import debtbundler.domain.entity.Debt;
import debtbundler.domain.entity.Person;
import debtbundler.presentation.debtviewer.DebtViewer;

/**
 * A background task that calculates debts for the specified persons and 
 * displays the results in a {@link DebtViewer}.
 * <p>
 * The <tt>DebtViewer</tt> will be added to a specified component. 
 * While calculating, this task disables the component and displays an 
 * indeterminate progress bar in it. When done, replaces the progress 
 * bar panel with a {@link DebtViewer} and restores the enabled/disabled 
 * status of the component.
 * 
 * @author Anssi
 *
 */
public class SolverTask extends SwingWorker<DebtViewer, Void> {
	
	private static Logger logger = Logger.getLogger(SolverTask.class.getName()); 
	
	private Collection<Person> persons;
	private Container container;
	private JPanel pleaseWaitPanel;
	private boolean isContainerEnabled;
	
	/**
	 * Starts a new solver task and displays the results in a {@link DebtViewer}
	 * added to the specified container.
	 * 
	 * @param persons		the persons whose debts to calculate (not null)
	 * @param container		the container where to add a {@link DebtViewer} for displaying the results
	 */
	public SolverTask(Collection<Person> persons, Container container) {
		this.persons = persons;
		this.container = container;
		setShowPleaseWait();
	}
	
	protected void setShowPleaseWait() {
		isContainerEnabled = container.isEnabled();
		container.setEnabled(false);
		container.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		
		JLabel label = new JLabel("Odota, velkoja lasketaan...");
		JProgressBar progressBar = new JProgressBar();
		progressBar.setIndeterminate(true);
		progressBar.setSize(label.getPreferredSize().width, label.getPreferredSize().height);
		
		Box pleaseWaitBox = Box.createVerticalBox();
		pleaseWaitBox.add(label);
		pleaseWaitBox.add(Box.createRigidArea( new Dimension(0, 6) ));
		pleaseWaitBox.add(progressBar);
		pleaseWaitBox.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
		
		pleaseWaitPanel = new JPanel();
		pleaseWaitPanel.setLayout(new BorderLayout());
		pleaseWaitPanel.add(pleaseWaitBox, BorderLayout.CENTER);
		
		container.add(pleaseWaitPanel);
	}

	@Override
	protected DebtViewer doInBackground() throws Exception {
		try {
			HybridSolver solver = new HybridSolver();
			solver.solve(persons);
			Collection<Debt> debts = solver.getDebts();
			List<Debt> sortedDebts = Utils.sortInPoolOrder(debts, persons);
			DebtViewer viewer = new DebtViewer(sortedDebts);
			return viewer;
		} catch (Exception e) {
			logger.log(Level.SEVERE, "Error when calculating debts.", e);
			throw e;
		}
		
	}
	
    @Override
    protected void done() {
    	// this method is executed even if doInBackground() throws an exception 
        try {
        	container.setEnabled(isContainerEnabled);
			container.setCursor(Cursor.getDefaultCursor());
        	container.remove(pleaseWaitPanel);
        	
			DebtViewer viewer = get();
			container.add(viewer);
			container.validate();
		} catch (Exception e) {
			logger.log(Level.SEVERE, "Error when adding DebtViewer to container.", e);
			RuntimeException ex = new RuntimeException(e);
			throw ex;
		} 
		
    }


}
